#include "queue.h"

#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/wait.h>

#include "log.h"

uint queue_interval = 1000;

struct jit_queue_context {
    bool active;
    wait_queue_head_t wait;
};

static void* jit_queue_start(struct seq_file *seq, loff_t *pos) {
    LOGI("jitqueue start pos: %lld", *pos);
    struct jit_queue_context *ctx = seq->private;
    if (!ctx) {
        ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
        if (!ctx) {
            LOGE("out of memory");
            return NULL;
        }
        LOGI("alloc new context 0x%p", ctx);
        ctx->active = true;
        init_waitqueue_head(&ctx->wait);
        seq->private = ctx;
    }
    LOGI("jitqueue start exit");
    return ctx;
}

static void* jit_queue_next(struct seq_file *seq, void *v, loff_t *pos) {
    LOGI("jitqueue next pos: %lld", *pos);
    struct jit_queue_context *ctx = seq->private;

    if (signal_pending(current) || !ctx->active) {
        LOGI("interrupted");
        ctx->active = false;
        return NULL;
    }

    (void)(*pos)++;
    LOGI("jitqueue next exit pos: %lld", *pos);
    return ctx;
}

static int jit_queue_show(struct seq_file *seq, void *v) {
    LOGI("jitqueue show");
    struct jit_queue_context *ctx = seq->private;
    ulong begin = jiffies;
    ulong duration = msecs_to_jiffies(queue_interval);
    long ret = 0;

    // wait event
    ret = wait_event_interruptible_timeout(ctx->wait, 0, duration);
    if (ret > 0) {
        LOGI("wait interrupted");
        ctx->active = false;
        return 0;
    } else if (ret < 0) {
        LOGI("wait failed");
        return -ERESTARTSYS;
    }
    seq_printf(seq, "% 12ld% 12ld\n", begin, jiffies);
    return 0;
}

static void jit_queue_stop(struct seq_file *seq, void *v) {
    LOGI("jitqueue stop");
    struct jit_queue_context *ctx = seq->private;

    if (ctx) {
        LOGI("free context 0x%p", ctx);
        kfree(ctx);
        seq->private = NULL;
    }
    LOGI("jitqueue stop exit");
}

static const struct seq_operations jit_queue_ops = {
    .start = jit_queue_start,
    .next = jit_queue_next,
    .show = jit_queue_show,
    .stop = jit_queue_stop,
};

static int jit_queue_open(struct inode *inode, struct file *filp) {
    return seq_open(filp, &jit_queue_ops);
}

// For the situation having only a single record,
// we can use single_open/release impl.
const struct proc_ops queue_ops = {
    .proc_open = jit_queue_open,
    .proc_read = seq_read,
    .proc_lseek = seq_lseek,
    .proc_release = seq_release,
};